
package com.dave.gd.common.util.xml;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;

import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.dave.gd.common.exception.ApplictionRuntimeException;
import com.dave.gd.common.util.ReflectUtil;
import com.dave.gd.common.util.StringHelper;
import com.dave.gd.common.util.spring.SpringBeanUtils;
import com.dave.gd.common.util.threadlocal.IExpCodes;
import com.dave.gd.common.util.xml.model.XmlBean;
import com.dave.gd.common.util.xml.model.XmlCapacity;
import com.dave.gd.common.util.xml.type.ITypeConverter;
import com.dave.gd.common.util.xml.type.TypeFactory;

/**
 * XmlBean和XMl转换工具类
 * 
 * 
 */
public final class XmlBeanUtil {
	private static final ThreadLocal<Boolean> isOutputZeroThread = new ThreadLocal<Boolean>() {
		@Override
		protected Boolean initialValue() {
			return true;
		}
	};// 数值类型节点是0是否输出该节点

	public static void setOutputZero(boolean isOutputZero) {
		isOutputZeroThread.set(isOutputZero);
	}

	private static boolean isOutputZero() {
		return isOutputZeroThread.get();
	}

	/**
	 * 输出XML
	 * 
	 * @param ele
	 *            dom4j元素(Element)
	 * @param encoding
	 *            编码
	 * @return xml报文
	 */
	public static String ele2Xml(Element ele, String encoding) {
		Document doc = DocumentHelper.createDocument(ele);
		doc.setXMLEncoding(encoding);
		OutputFormat format = new OutputFormat();
		format.setTrimText(true);
		format.setNewlines(true); // 设置是否换行
		format.setNewLineAfterDeclaration(false);
		format.setIndent(true); // 设置是否缩进
		format.setIndent(" ");// 以空格方式实现缩进
		if (StringHelper.isNotEmpty(encoding)) {
			format.setEncoding(encoding);
		}

		StringWriter sw = new StringWriter();
		XMLWriter xmlWriter = new XMLWriter(sw, format);
		try {
			xmlWriter.write(doc);
			return sw.toString();
		} catch (IOException e) {
			throw new ApplictionRuntimeException(IExpCodes.WSAPP_XML_PARSE_ERROR,"输出XMLIO异常!", e).addParam("errMsg", e.getMessage());
		} finally {
			try {
				if (xmlWriter != null) {
					xmlWriter.flush();
					xmlWriter.close();
				}
			} catch (IOException e) {
			}
			IOUtils.closeQuietly(sw);
		}
	}

	/**
	 * XML转Document
	 * 
	 * @param xml
	 *            xml报文
	 * @return dom4j Document对象
	 */
	public static Document xml2Doc(String xml) {
		try {
			return DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			throw new ApplictionRuntimeException(IExpCodes.WSAPP_XML_PARSE_ERROR, "报文格式错误!",
					e).addParam("errMsg", e.getMessage());
		}
	}

	/**
	 * XML转Element
	 * 
	 * @param xml
	 *            xml报文
	 * @return dom4j元素(Element)
	 */
	public static Element xml2Ele(String xml) {
		Document doc = xml2Doc(xml);
		return doc.getRootElement();
	}

	/**
	 * 序列化XmlBean
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @return dom4j元素(Element)
	 */
	public static Element serialize(XmlBean bean) {
		return serialize(bean, getElementName(bean));
	}

	/**
	 * 序列化XmlBean
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param eleName
	 *            指定的dom4j元素名称
	 * @return dom4j元素(Element)
	 */
	public static Element serialize(XmlBean bean, String eleName) {
		Element localEle = DocumentHelper.createElement(eleName);
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			String[] propOrder = anntType.propOrder();
			if (propOrder != null && propOrder.length > 0
					&& StringHelper.isNotEmpty(propOrder[0])) {
				for (String prop : propOrder) {
					PropertyDescriptor pd = SpringBeanUtils.getPropertyDescriptor(
							bean.getClass(), prop);
					if (pd != null) {
						serialize(bean, pd, localEle);
					}
				}
				return localEle;
			}
		}

		PropertyDescriptor[] pds = SpringBeanUtils.getPropertyDescriptors(bean
				.getClass());
		for (PropertyDescriptor pd : pds) {
			serialize(bean, pd, localEle);
		}
		return localEle;
	}

	/**
	 * 序列化
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param pd
	 *            XmlBean对象属性
	 * @param eleName
	 *            指定的dom4j元素名称
	 */
	private static void serialize(XmlBean bean, PropertyDescriptor pd,
			Element eleName) {
		if (getAnnotation(pd, XmlTransient.class) != null) {
			return;// 当前属性不序列化
		}

		Class<?> cls = pd.getPropertyType();
		if (cls == null) {
			return;
		}

		// 下一级节点
		if (XmlBean.class.isAssignableFrom(cls)) {
			XmlBean child = (XmlBean) SpringBeanUtils.getPropertyValue(bean, pd);
			if (child != null) {
				Element childEle = serialize(child, getElementName(bean, pd));
				if (childEle != null) {
					eleName.add(childEle);
				}
			}
			return;
		}

		// XML容器
		if (XmlCapacity.class.isAssignableFrom(cls)) {
			XmlCapacity capty = (XmlCapacity) SpringBeanUtils.getPropertyValue(bean,
					pd);
			if (capty != null) {
				capty.serialize(eleName, getElementName(bean, pd));
			}
		}

		// XML元素
		if (Element.class.isAssignableFrom(cls)) {
			Element childEle = (Element) SpringBeanUtils.getPropertyValue(bean, pd);
			if (childEle != null) {
				childEle.setParent(null);
				eleName.add(childEle);
			}
		}

		// 基本类型
		ITypeConverter type = TypeFactory.getType(cls);
		if (type == null) {
			return;// 不支持的类型直接忽略
		}

		Object value = SpringBeanUtils.getPropertyValue(bean, pd);
		if (value == null) {
			return;
		}
		if (Number.class.isAssignableFrom(value.getClass())) {
			// 数值类型
			Number num = (Number) value;
			if (0 == num.longValue() && !isOutputZero()) {
				return;
			}
		}

		String text = type.getText(value);

		// 属性
		XmlAttribute anntAttr = getAnnotation(pd, XmlAttribute.class);
		if (anntAttr != null) {
			if (StringHelper.isNotEmpty(text)) {
				String attrName = anntAttr.name();
				if (StringHelper.isEmpty(attrName)
						|| "##default".equals(attrName)) {
					attrName = getElementName(bean, pd);
				}
				eleName.addAttribute(attrName, text);
			}
			return;
		}

		// 值
		XmlValue anntValue = getAnnotation(pd, XmlValue.class);
		if (anntValue != null) {
			if (StringHelper.isNotEmpty(text)) {
				eleName.addText(text);
			}
			return;
		}

		// 元素
		Element textEle = eleName.addElement(getElementName(bean, pd));
		if (StringHelper.isNotEmpty(text)) {
			textEle.addText(text);
		}
	}

	/**
	 * 指定的dom4j元素名称
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param eleName
	 *            指定的dom4j元素名称
	 */
	public static void unserialize(XmlBean bean, Element eleName) {
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			String[] propOrder = anntType.propOrder();
			if (propOrder != null && propOrder.length > 0
					&& StringHelper.isNotEmpty(propOrder[0])) {
				for (String prop : propOrder) {
					PropertyDescriptor pd = SpringBeanUtils.getPropertyDescriptor(
							bean.getClass(), prop);
					if (pd != null) {
						unserialize(bean, pd, eleName);
					}
				}
				return;
			}
		}

		PropertyDescriptor[] pds = SpringBeanUtils.getPropertyDescriptors(bean
				.getClass());
		for (PropertyDescriptor pd : pds) {
			unserialize(bean, pd, eleName);
		}
	}

	/**
	 * 反序列化
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param pd
	 *            XmlBean对象属性
	 * @param ele
	 *            指定的dom4j元素
	 */
	private static void unserialize(XmlBean bean, PropertyDescriptor pd,
			Element ele) {
		if (getAnnotation(pd, XmlTransient.class) != null) {
			return;// 当前属性不序列化
		}

		Class<?> cls = pd.getPropertyType();

		// 下一级节点
		if (XmlBean.class.isAssignableFrom(cls)) {
			Element childEle = ele.element(getElementName(bean, pd));
			if (childEle != null) {
				XmlBean child = (XmlBean) newInstance(cls);
				if (child != null) {
					unserialize(child, childEle);
					SpringBeanUtils.setPropertyValue(bean, pd, child);
				}
			}
			return;
		}

		// XML容器
		if (XmlCapacity.class.isAssignableFrom(cls)) {
			List<?> childEles = ele.elements(getElementName(bean, pd));
			if (childEles == null || childEles.size() <= 0) {
				return;
			}

			XmlCapacity capty = (XmlCapacity) SpringBeanUtils.getPropertyValue(bean,
					pd);
			if (capty == null) {
				capty = (XmlCapacity) newInstance(cls);
				SpringBeanUtils.setPropertyValue(bean, pd, capty);
			}
			if (capty != null) {
				for (int i = 0; i < childEles.size(); i++) {
					Element childEle = (Element) childEles.get(i);
					capty.unserialize(childEle);
				}
			}
		}

		// XML元素
		if (Element.class.isAssignableFrom(cls)) {
			Element childEle = ele.element(getElementName(bean, pd));
			if (childEle != null) {
				SpringBeanUtils.setPropertyValue(bean, pd, childEle);
			}
		}

		// 基本类型
		ITypeConverter type = TypeFactory.getType(cls);
		if (type == null) {
			return;// 不支持的类型直接忽略
		}

		// 属性
		XmlAttribute anntAttr = getAnnotation(pd, XmlAttribute.class);
		if (anntAttr != null) {
			String attrName = anntAttr.name();
			if (StringHelper.isEmpty(attrName) || "##default".equals(attrName)) {
				attrName = getElementName(bean, pd);
			}
			String text = ele.attributeValue(attrName);
			if (StringHelper.isNotEmpty(text)) {
				Object value = type.getValue(text);
				if (value != null) {
					SpringBeanUtils.setPropertyValue(bean, pd, value);
				}
			}
			return;
		}

		// 值
		XmlValue anntValue = getAnnotation(pd, XmlValue.class);
		if (anntValue != null) {
			String text = ele.getText();
			if (StringHelper.isNotEmpty(text)) {
				Object value = type.getValue(text);
				if (value != null) {
					SpringBeanUtils.setPropertyValue(bean, pd, text);
				}
			}
			return;
		}

		// 元素
		String eleName = getElementName(bean, pd);
		Element cldEle = ele.element(eleName);
		String text = null;
		if (cldEle != null) {
			text = cldEle.getText();
			if (StringHelper.isEmpty(text) && cldEle.hasContent()) {
				text = cldEle.getStringValue();
			}
		}

		if (StringHelper.isNotEmpty(text)) {
			Object value = type.getValue(text);
			if (value != null) {
				SpringBeanUtils.setPropertyValue(bean, pd, value);
			}
		}
	}

	/**
	 * 实例化
	 * 
	 * @param cls
	 *            要实例化的java类
	 * @return 实例化对象
	 */
	private static Object newInstance(Class<?> cls) {
		return ReflectUtil.newInstance(cls);
	}

	/**
	 * 获取元素名称
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @return 元素名称
	 */
	public static String getElementName(XmlBean bean) {
		String name = null;
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			name = anntType.name();
		}
		if (StringHelper.isEmpty(name)) {
			name = bean.getClass().getSimpleName();
		}
		return name;
	}

	/**
	 * 获取元素名称(属性)
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param pd
	 *            XmlBean对象属性
	 * @return 元素名称
	 */
	private static String getElementName(XmlBean bean, PropertyDescriptor pd) {
		String eleName = null;
		XmlElement anntEle = getAnnotation(pd, XmlElement.class);
		if (anntEle != null) {
			eleName = anntEle.name();
		}

		if (StringHelper.isEmpty(eleName) || "##default".equals(eleName)) {
			return StringHelper.first2Upper(pd.getName());
		}
		return eleName;
	}

	/**
	 * 获取getter方法上的注解对象
	 * 
	 * @param bean
	 *            XmlBean对象
	 * @param pd
	 *            XmlBean对象属性
	 * @param annotationClass
	 *            注解对象类
	 * @return 注解对象
	 */
	private static <T extends Annotation> T getAnnotation(
			PropertyDescriptor pd, Class<T> annotationClass) {
		try {
			// 取getter上的注解
			Method method = pd.getReadMethod();
			if (method != null) {
				return method.getAnnotation(annotationClass);
			}
		} catch (SecurityException e) {
		}
		return null;
	}

}
